package com.ruben.utils.server;

import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.GlobalMemory;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.software.os.FileSystem;
import oshi.software.os.OSFileStore;
import oshi.software.os.OperatingSystem;
import oshi.util.Util;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * 服务器信息
 *
 * @author Administrator
 */
public class ServerOsInfo {


    public static ServerInfo getInfo() {
        ServerInfo info = new ServerInfo();
        SystemInfo si = new SystemInfo();
        HardwareAbstractionLayer hal = si.getHardware();
        CentralProcessor processor = hal.getProcessor();  //获取cpu信息
        info.setCpu(setCpuInfo(processor));
        GlobalMemory memory = hal.getMemory();  //获取内存信息
        info.setMem(setMemInfo(memory));
        info.setSys(setSysInfo()); //服务器信息
        info.setJvm(setJvmInfo()); //jvm信息
        OperatingSystem op = si.getOperatingSystem();
        info.setFile(setSysFiles(op)); //磁盘信息
        info.setIp(getHostIp());
        info.setHostname(getHostName());
        return info;
    }

    /*
     * cpu信息
     *
     * @param processor
     */
    private static ServerInfo.Cpu setCpuInfo(CentralProcessor processor) {   // CPU信息
        long[] prevTicks = processor.getSystemCpuLoadTicks();
        Util.sleep(1000);
        long[] ticks = processor.getSystemCpuLoadTicks();
        double nice = ticks[CentralProcessor.TickType.NICE.getIndex()] - prevTicks[CentralProcessor.TickType.NICE.getIndex()];
        double irq = ticks[CentralProcessor.TickType.IRQ.getIndex()] - prevTicks[CentralProcessor.TickType.IRQ.getIndex()];
        double softirq = ticks[CentralProcessor.TickType.SOFTIRQ.getIndex()] - prevTicks[CentralProcessor.TickType.SOFTIRQ.getIndex()];
        double steal = ticks[CentralProcessor.TickType.STEAL.getIndex()] - prevTicks[CentralProcessor.TickType.STEAL.getIndex()];
        /*
         * CPU系统使用率
         */
        double sys = ticks[CentralProcessor.TickType.SYSTEM.getIndex()] - prevTicks[CentralProcessor.TickType.SYSTEM.getIndex()];
        /*
         * CPU用户使用率
         */
        double used = ticks[CentralProcessor.TickType.USER.getIndex()] - prevTicks[CentralProcessor.TickType.USER.getIndex()];
        /*
         * CPU当前等待率
         */
        double iowait = ticks[CentralProcessor.TickType.IOWAIT.getIndex()] - prevTicks[CentralProcessor.TickType.IOWAIT.getIndex()];
        /*
         * CPU当前空闲率
         */
        double free = ticks[CentralProcessor.TickType.IDLE.getIndex()] - prevTicks[CentralProcessor.TickType.IDLE.getIndex()];
        /*
         * CPU总的使用率
         */
        double total = used + nice + sys + free + iowait + irq + softirq + steal;

        /*
         * 核心数
         */
        int cpuNum = processor.getLogicalProcessorCount();

        /*
         * CPU当前等待率
         */

        ServerInfo.Cpu cpu = new ServerInfo.Cpu();
        cpu.setCpucard(processor);//cpu信息
        cpu.setNum(cpuNum);//cpu核心数
        cpu.setUsed(round(mul((sys + used) / total, 100), 2));//cpu 用户使用率;
        cpu.setFree(round(mul(free / total, 100), 2));//cpu 空闲率
        return cpu;
    }

    /**
     * 内存信息
     */
    private static ServerInfo.MemoryInfo setMemInfo(GlobalMemory memory) {
        /*
         * 内存总量
         */
        double total = +memory.getTotal();

        /*
         * 已用内存
         */
        double used = memory.getTotal() - memory.getAvailable();

        /*
         * 剩余内存
         */
        double free = memory.getAvailable();
        ServerInfo.MemoryInfo mem = new ServerInfo.MemoryInfo();
        mem.setTotal(div(total, (1024 * 1024 * 1024), 2));// 内存总大小
        mem.setUsedMem(div(used, (1024 * 1024 * 1024), 2));// 已使用内存大小
        mem.setFree(round(mul(free / total, 100), 2));// 剩余内存大小
        mem.setUsed(round(mul(used / total, 100), 2));
        return mem;
    }

    /*
     * 服务器信息
     */
    private static Properties setSysInfo() {
        return System.getProperties();
    }


    /*
     * Java虚拟机
     */
    private static ServerInfo.Jvm setJvmInfo() {
        ServerInfo.Jvm jvm = new ServerInfo.Jvm();
        jvm.setTotal(div(Runtime.getRuntime().totalMemory(), 1024 * 1024, 2));
        jvm.setMaxTotal(div(Runtime.getRuntime().maxMemory(), 1024 * 1024, 2));
        jvm.setUsedMem(div((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()), 1024 * 1024, 2));
        jvm.setUsed(round(mul((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) * 1.0 / Runtime.getRuntime().totalMemory(), 100), 2));
        return jvm;
    }

    /*
     * 设置磁盘信息
     */
    private static List<ServerInfo.FileItem> setSysFiles(OperatingSystem os) {
        FileSystem fileSystem = os.getFileSystem();
        OSFileStore[] fsArray = fileSystem.getFileStores();
        List<ServerInfo.FileItem> list = new ArrayList<>();
        for (OSFileStore fs : fsArray) {
            long free = fs.getUsableSpace();
            long total = fs.getTotalSpace();
            long used = total - free;
            ServerInfo.FileItem fileItem = new ServerInfo.FileItem();
            fileItem.setPath(fs.getMount());
            fileItem.setType(fs.getType());
            fileItem.setName(fs.getName());
            fileItem.setTotal(convertFileSize(total));
            fileItem.setFree(convertFileSize(free));
            fileItem.setUsed(convertFileSize(used));
            fileItem.setRate(div(used, total, 4) * 100);
            list.add(fileItem);
        }
        return list;
    }

    /*
     * 字节转换
     *
     * @param size 字节大小
     * @return 转换后值
     */
    public static String convertFileSize(long size) {
        long kb = 1024;
        long mb = kb * 1024;
        long gb = mb * 1024;
        if (size >= gb) {
            return String.format("%.1f GB", (float) size / gb);
        } else if (size >= mb) {
            float f = (float) size / mb;
            return String.format(f > 100 ? "%.0f MB" : "%.1f MB", f);
        } else if (size >= kb) {
            float f = (float) size / kb;
            return String.format(f > 100 ? "%.0f KB" : "%.1f KB", f);
        } else {
            return String.format("%d B", size);
        }
    }

    /*
     * 提供（相对）精确的除法运算。当发生除不尽的情况时，由scale参数指
     * 定精度，以后的数字四舍五入。
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 表示表示需要精确到小数点以后几位。
     * @return 两个参数的商
     */
    public static double div(double v1, double v2, int scale) {
        return ((double) ((int) (v1 / v2 * Math.pow(10, scale)))) / Math.pow(10, scale);

    }

    /**
     * 提供精确的小数位四舍五入处理。
     *
     * @param v     需要四舍五入的数字
     * @param scale 小数点后保留几位
     * @return 四舍五入后的结果
     */
    public static double round(double v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b = new BigDecimal(Double.toString(v));
        BigDecimal one = new BigDecimal("1");
        return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 提供精确的乘法运算。
     *
     * @param v1 被乘数
     * @param v2 乘数
     * @return 两个参数的积
     */
    public static double mul(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.multiply(b2).doubleValue();
    }

    public static String getHostIp() {
        try {
            return InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException ignored) {
        }
        return "127.0.0.1";
    }

    public static String getHostName() {
        try {
            return InetAddress.getLocalHost().getHostName();
        } catch (UnknownHostException ignored) {
        }
        return "未知";
    }

    public static void main(String[] args) {
        System.out.println(div(19, 7, 4));
        System.out.println(getInfo());
    }
}
